//*******************************************************************************
// COPYRIGHT NOTES
// ---------------
// This is a sample for BCGControlBar Library Professional Edition
// Copyright (C) 1998-2016 BCGSoft Ltd.
// All rights reserved.
//
// This source code can be used, distributed or modified
// only under terms and conditions 
// of the accompanying license agreement.
//*******************************************************************************
//
// ChartInteractiveView.cpp : implementation file
//

#include "stdafx.h"
#include "bcgpchartexample.h"
#include "ChartInteractiveView.h"
#include "BCGPMath.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

const double dblPressureBase = 1020.0;

class CChartSeriesPressure : public CBCGPChartAreaSeries
{
public:
	CChartSeriesPressure(CBCGPChartVisualObject* pChartCtrl, const CString& strSeriesName) :
		CBCGPChartAreaSeries(pChartCtrl, strSeriesName)
	{
	}

	virtual BOOL GetDataPointTableText(int nDataPointIndex, CString& strDPLabel, int nPrecision = -1)
	{
		UNREFERENCED_PARAMETER(nPrecision);

		CBCGPChartValue val = GetDataPointValue(nDataPointIndex, CBCGPChartData::CI_Y);

		strDPLabel = val.GetValue() > dblPressureBase ? _T("Hi") : _T("Lo");
		return TRUE;
	}

	virtual void GetDataTableName(CString& strName) 
	{ 
		strName = m_strSeriesName + _T(" (High/Low)"); 
	}
};

/////////////////////////////////////////////////////////////////////////////
// CChartInteractiveView

#define DEFAULT_INFO	_T("Move the mouse cursor over the chart and see information on hovered chart element")

static const CBCGPSize m_szMarker(5, 5);

IMPLEMENT_DYNCREATE(CChartInteractiveView, CBCGPChartExampleView)

CChartInteractiveView::CChartInteractiveView()
	: CBCGPChartExampleView(CChartInteractiveView::IDD)
{
	//{{AFX_DATA_INIT(CChartInteractiveView)
	m_strInfo = DEFAULT_INFO;
	//}}AFX_DATA_INIT

	m_pAxisY1 = NULL;
	m_pAxisY2 = NULL;

	m_XLine = -1.;
	m_dblDataTableColumnWidth = 0.0;
}

CChartInteractiveView::~CChartInteractiveView()
{
}

void CChartInteractiveView::DoDataExchange(CDataExchange* pDX)
{
	CBCGPChartExampleView::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CChartInteractiveView)
	DDX_Control(pDX, IDC_CHART, m_wndChart);
	DDX_Text(pDX, IDC_INFO, m_strInfo);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CChartInteractiveView, CBCGPChartExampleView)
	//{{AFX_MSG_MAP(CChartInteractiveView)
	//}}AFX_MSG_MAP
	ON_REGISTERED_MESSAGE(BCGM_ON_CHART_MOUSE_TRACK, OnMouseTrack)
	ON_REGISTERED_MESSAGE(BCGM_ON_CHART_AFTER_DRAW, OnAfterDraw)
	ON_REGISTERED_MESSAGE(BCGM_ON_CHART_AFTER_RECALC_LAYOUT, OnAfterRecalcLayout)
	ON_REGISTERED_MESSAGE(BCGM_ON_CHART_MOUSE_UP, OnChartMouseUp)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CChartInteractiveView diagnostics

#ifdef _DEBUG
void CChartInteractiveView::AssertValid() const
{
	CBCGPChartExampleView::AssertValid();
}

void CChartInteractiveView::Dump(CDumpContext& dc) const
{
	CBCGPChartExampleView::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CChartInteractiveView message handlers

void CChartInteractiveView::OnInitialUpdate() 
{
	CBCGPChartExampleView::OnInitialUpdate();
	
	if (m_bIsReady)
	{
		return;
	}

	m_bIsReady = TRUE;
	
	CBCGPInfoTipOptions infoTipOptions;
	infoTipOptions.m_StemLocation = CBCGPPopupWindow::BCGPPopupWindowStemLocation_BottomCenter;
	
	m_wndChart.EnableInfoTip(TRUE, &infoTipOptions);

	CBCGPChartVisualObject* pChart = m_wndChart.GetChart();
	ASSERT_VALID(pChart);

	pChart->SetChartTitle(_T("Weather Report"));

	pChart->EnableMouseTrackingMode(
		BCGPChartHitInfo::HIT_DATA_POINT |
		BCGPChartHitInfo::HIT_DATA_LABEL |
		BCGPChartHitInfo::HIT_AXIS |
		BCGPChartHitInfo::HIT_AXIS_NAME |
		BCGPChartHitInfo::HIT_CHART_AREA |
		BCGPChartHitInfo::HIT_LEGEND |
		BCGPChartHitInfo::HIT_TITLE |
		BCGPChartHitInfo::HIT_DIAGRAM |
		BCGPChartHitInfo::HIT_DATA_TABLE);

	pChart->SetHitTestDataPointPrecision(m_szMarker);

	// Add series and data points:
	CBCGPChartSeries* pSeries1 = pChart->CreateSeries(_T("Temperature"), CBCGPColor(), BCGP_CT_DEFAULT, BCGPChartLine);

	CBCGPChartSeries* pSeries2 = new CChartSeriesPressure(pChart, _T("Pressure"));
	pChart->AddSeries(pSeries2);

	CBCGPChartSeries* pSeries3 = pChart->CreateSeries(_T("Humidity"), CBCGPColor(), BCGP_CT_DEFAULT, BCGPChartColumn);

	for (int nDay = 1; nDay <= 31; nDay++)
	{
		pSeries1->AddDataPoint((int)(20. + Rand(-5., 5.)));
		pSeries2->AddDataPoint(dblPressureBase + Rand(-4., 4.));
		pSeries3->AddDataPoint(50. + Rand(-40., 40.));

		int nIndex = nDay - 1;

		if ((nIndex % 3) == 0)
		{
			pSeries1->ShowDataLabel(TRUE, nIndex);
			pSeries1->SetDataLabelAngle(0, nIndex);
			pSeries1->ShowMarker(TRUE, nIndex);
			pSeries1->SetMarkerSize(4, nIndex);
			pSeries1->SetDataLabelDropLineToMarker(FALSE, nIndex);
		}
	}

	// Setup Main Axes:
	CBCGPChartAxis* pXAxis = pChart->GetChartAxis(BCGP_CHART_X_PRIMARY_AXIS);
	ASSERT_VALID(pXAxis);

	pXAxis->m_strAxisName = _T("Day");

	pXAxis->UsePerpendicularAxisForZoning(FALSE);

	CBCGPChartAxis* pYAxis = pChart->GetChartAxis(BCGP_CHART_Y_PRIMARY_AXIS);
	ASSERT_VALID(pYAxis);

	pYAxis->m_strAxisName = _T("Humidity,%");
	pYAxis->m_bDisplayAxisName = TRUE;

	// Setup Custom Axes:
	m_pAxisY1 = (CBCGPChartAxisY*) pYAxis->Split(33, 6);
	m_pAxisY2 = (CBCGPChartAxisY*) pYAxis->Split(50, 6);

	m_pAxisY1->SetFixedDisplayRange(15, 25);

	m_pAxisY1->m_strAxisName = _T("Temperature");
	m_pAxisY1->m_bDisplayAxisName = TRUE;

	m_pAxisY2->m_strAxisName = _T("Pressure");
	m_pAxisY2->m_bDisplayAxisName = TRUE;

	// Associate 2 first series with the custom axes:
	pSeries1->SetRelatedAxis(m_pAxisY1, CBCGPChartSeries::AI_Y);
	pSeries2->SetRelatedAxis(m_pAxisY2, CBCGPChartSeries::AI_Y);

	// Create line attributes:
	m_brLine.SetColor(CBCGPColor::Red);
	m_strokeStyle.SetDashStyle(CBCGPStrokeStyle::BCGP_DASH_STYLE_DASH);

	// Create data table marker brush:
	m_brDataTable.SetColor(CBCGPColor::LightSalmon, .2);

	pChart->EnableResizeAxes(TRUE);

	BCGPChartFormatDataTable formatDataTable;
	formatDataTable.m_bInterlaceRows = TRUE;
	formatDataTable.SetContentPadding(CBCGPSize(1., 5.));

	pChart->ShowDataTable(TRUE, &formatDataTable);

	pChart->Redraw();
}

LRESULT CChartInteractiveView::OnMouseTrack(WPARAM /*wp*/, LPARAM lp)
{
	CBCGPChartVisualObject* pChart = m_wndChart.GetChart();
	ASSERT_VALID(pChart);

	BCGPChartHitInfo* pHitInfo = (BCGPChartHitInfo*)lp;
	CBCGPPoint pt = pHitInfo->m_ptHit;

	CBCGPChartAxis* pXAxis = pChart->GetChartAxis(BCGP_CHART_X_PRIMARY_AXIS);
	ASSERT_VALID(pXAxis);

	// Get X Position on the chart
	double dblXMarker = pXAxis->ValueFromPoint(pt);

	// left marker index
	int nXValue = (int) floor(dblXMarker - 1);

	BOOL bRedraw = FALSE;

	const int nSeriesCount = pChart->GetSeriesCount();

	if (m_arMarkers.GetSize() == 0)
	{
		m_arMarkers.SetSize(nSeriesCount);
	}

	for (int i = 0; i < nSeriesCount; i++)
	{
		CBCGPChartSeries* pSeries = pChart->GetSeries(i);
		ASSERT_VALID(pSeries);

		CBCGPChartAxis* pYAxis = pSeries->GetRelatedAxis(CBCGPChartSeries::AI_Y);
		ASSERT_VALID(pYAxis);

		const CBCGPChartDataPoint* pDP1 = pSeries->GetDataPointAt(nXValue);
		const CBCGPChartDataPoint* pDP2 = pSeries->GetDataPointAt(nXValue + 1);

		if (pDP1 != NULL && pDP2 != NULL)
		{
			CBCGPPoint pt1 = pSeries->GetDataPointScreenPoint(nXValue);
			CBCGPPoint pt2 = pSeries->GetDataPointScreenPoint(nXValue + 1);
			CBCGPPoint ptMarker;

			if (fabs(pt.x - pt1.x) < fabs(pt.x - pt2.x))
			{
				ptMarker = pt1;
			}
			else
			{
				ptMarker = pt2;
			}

			if (m_arMarkers[i] != ptMarker)
			{
				m_arMarkers[i] = ptMarker;

				if (i == 0)
				{
					m_XLine = ptMarker.x;
					m_dblDataTableColumnWidth = fabs(pt2.x - pt1.x);
				}

				bRedraw = TRUE;
			}
		}
	}

	if (bRedraw)
	{
		pChart->Redraw();
	}

	switch (pHitInfo->m_hitInfo)
	{
	case BCGPChartHitInfo::HIT_DATA_POINT:
		m_strInfo = _T("Hovered Element: Data Point\nClick left mouse button to get additional information");
		::SetCursor (globalData.GetHandCursor ());
		break;

	case BCGPChartHitInfo::HIT_AXIS:
	case BCGPChartHitInfo::HIT_AXIS_NAME:
		{
			pXAxis = pChart->GetChartAxis(BCGP_CHART_X_PRIMARY_AXIS);
			ASSERT_VALID(pXAxis);

			CBCGPChartAxis* pYAxis = pChart->GetChartAxis(BCGP_CHART_Y_PRIMARY_AXIS);
			ASSERT_VALID(pYAxis);

			CString strName = _T("???");
			double dblVal = 0.;

			CBCGPChartAxis* pAxis = NULL;

			if (pXAxis->m_nAxisID == pHitInfo->m_nIndex1)
			{
				pAxis = pXAxis;
			}
			else if (pYAxis->m_nAxisID == pHitInfo->m_nIndex1)
			{
				pAxis = pYAxis;
				strName = pYAxis->m_strAxisName;
			}
			else if (m_pAxisY1->m_nAxisID == pHitInfo->m_nIndex1)
			{
				pAxis = m_pAxisY1;
			}
			else if (m_pAxisY2->m_nAxisID == pHitInfo->m_nIndex1)
			{
				pAxis = m_pAxisY2;
				strName = m_pAxisY2->m_strAxisName;
			}

			if (pAxis != NULL)
			{
				strName = pAxis->m_strAxisName;
				dblVal = pAxis->ValueFromPoint(pt);
			}

			if (pHitInfo->m_hitInfo == BCGPChartHitInfo::HIT_AXIS_NAME)
			{
				m_strInfo.Format(_T("Hovered Element: Axis Name\r\nAxis Name: %s"), strName);
			}
			else
			{
				m_strInfo.Format(_T("Hovered Element: Axis\r\nAxis Name: %s\r\nValue = %d"), strName, bcg_round(dblVal));
			}
		}
		break;

	case BCGPChartHitInfo::HIT_CHART_AREA:
		m_strInfo = _T("Hovered Element: Chart Area");
		break;

	case BCGPChartHitInfo::HIT_LEGEND:
		m_strInfo = _T("Hovered Element: Legend");
		break;

	case BCGPChartHitInfo::HIT_TITLE:
		m_strInfo = _T("Hovered Element: Chart Title");
		break;

	case BCGPChartHitInfo::HIT_DATA_TABLE:
		if (pHitInfo->m_nIndex2 < 0)
		{
			m_strInfo.Format(_T("Hovered Element: Data Table Label\r\nSeries Index: %d\r\nSeries Name: %s"),
				pHitInfo->m_nIndex1,
				pChart->GetSeries(pHitInfo->m_nIndex1)->m_strSeriesName);
		}
		else
		{
			m_strInfo.Format(_T("Hovered Element: Date Table Value\r\nSeries Index: %d\r\nData Point Index: %d\r\nValue: %f"),
				pHitInfo->m_nIndex1,
				pHitInfo->m_nIndex2,
				(double)pChart->GetSeries(pHitInfo->m_nIndex1)->GetDataPointValue(pHitInfo->m_nIndex2));
		}
		break;
		
	case BCGPChartHitInfo::HIT_DIAGRAM:
		{
			pXAxis = pChart->GetChartAxis(pHitInfo->m_nIndex1);
			CBCGPChartAxis* pYAxis = pChart->GetChartAxis(pHitInfo->m_nIndex2);

			if (pXAxis != NULL && pYAxis != NULL)
			{
				ASSERT_VALID(pXAxis);
				ASSERT_VALID(pYAxis);

				m_strInfo.Format(_T("Hovered Element: Chart Diagram\r\nX Value: %d\r\nY Value: %f"),
					(int)(pXAxis->ValueFromPoint(pt) + 0.5),
					pYAxis->ValueFromPoint(pt));
			}
			else
			{
				m_strInfo.Format(_T("Hovered Element: Chart Diagram"));
			}
		}
		break;

	case BCGPChartHitInfo::HIT_DATA_LABEL:
		m_strInfo.Format(_T("Hovered Element: Data Label\r\nSeries Index: %d\r\nData Point Index: %d\r\nValue: %f"),
			pHitInfo->m_nIndex1,
			pHitInfo->m_nIndex2,
			(double)pChart->GetSeries(pHitInfo->m_nIndex1)->GetDataPointValue(pHitInfo->m_nIndex2));
		break;

	default:
		m_strInfo = DEFAULT_INFO;
		break;
	}

	UpdateData(FALSE);
	return 0;
}

LRESULT CChartInteractiveView::OnAfterDraw(WPARAM /*wp*/, LPARAM lp)
{
	CBCGPGraphicsManager* pGM = (CBCGPGraphicsManager*) lp;
	ASSERT_VALID(pGM);

	CBCGPChartVisualObject* pChart = m_wndChart.GetChart();
	ASSERT_VALID(pChart);

	// Draw markers:
	for (int i = 0; i < m_arMarkers.GetSize(); i++)
	{
		CBCGPChartSeries* pSeries = pChart->GetSeries(i);

		BCGPSeriesColorsPtr colors;
		pSeries->GetColors(colors, i);

		CBCGPRect rectAxisBounds = pSeries->GetAxesBoundingRect();
		CBCGPPoint ptMarker = m_arMarkers[i]; 

		if (rectAxisBounds.PtInRect(ptMarker))
		{
			CBCGPEllipse ellipse(ptMarker, m_szMarker.cx, m_szMarker.cy);
			pGM->FillEllipse(ellipse, *colors.m_pBrElementLineColor);
		}
	}

	// Draw vertical line and highlight data table column:
	if (m_XLine != -1.)
	{
		CBCGPRect rectDiagram = pChart->GetDiagramArea();

		pGM->DrawLine(m_XLine, rectDiagram.top, m_XLine, rectDiagram.bottom, m_brLine, 1., &m_strokeStyle);

		if (m_dblDataTableColumnWidth > 0.0)
		{
			CBCGPRect rectColumn = pChart->GetDataTableArea();
			rectColumn.left = m_XLine - m_dblDataTableColumnWidth / 2.0;
			rectColumn.right = rectColumn.left + m_dblDataTableColumnWidth;

			if (pGM->IsSupported(BCGP_GRAPHICS_MANAGER_COLOR_OPACITY))
			{
				pGM->FillRectangle(rectColumn, m_brDataTable);
			}
			else
			{
				rectColumn.left++;
				rectColumn.top++;

				pGM->DrawRectangle(rectColumn, m_brDataTable);
			}
		}
	}

	return 0;
}

LRESULT CChartInteractiveView::OnAfterRecalcLayout(WPARAM, LPARAM)
{
	m_XLine = -1.;
	m_dblDataTableColumnWidth = 0.0;
	m_arMarkers.RemoveAll();

	return 0;
}

LRESULT CChartInteractiveView::OnChartMouseUp(WPARAM /*wp*/, LPARAM lp)
{
	CBCGPChartVisualObject* pChart = m_wndChart.GetChart();
	ASSERT_VALID(pChart);

	BCGPChartHitInfo* pHitInfo = (BCGPChartHitInfo*)lp;
	if (pHitInfo->m_nMouseButton != 0 || 
		(pHitInfo->m_hitInfo != BCGPChartHitInfo::HIT_DATA_POINT && pHitInfo->m_hitInfo != BCGPChartHitInfo::HIT_DATA_TABLE))
	{
		return 0;
	}

	CBCGPChartSeries* pSeries = pChart->GetSeries(pHitInfo->m_nIndex1);
	ASSERT_VALID(pSeries);

	CString strName;
	if (pHitInfo->m_hitInfo == BCGPChartHitInfo::HIT_DATA_TABLE)
	{
		strName = _T("Data Table Cell");
	}
	else
	{
		strName = _T("Data Point");
	}

	CString str;
	str.Format(_T("Clicked on the %s:\r\n\nSeries Index: %d\r\nSeries Name: %s\r\nData Point Index: %d\r\nData Point Value: %f"),
		strName,
		pHitInfo->m_nIndex1,
		pSeries->m_strSeriesName,
		pHitInfo->m_nIndex2,
		(double)pSeries->GetDataPointValue(pHitInfo->m_nIndex2));
	MessageBox(str, NULL, MB_OK | MB_ICONASTERISK);

	return 1;
}
